Ruby 日記 31日目: オブジェクトの凍結と破壊的な操作と代入
次のプログラムを実行するとどうなりますか
code:gold/ex31/main.rb
array = array.map!{|content| content.succ}
p array
選択肢:
["a", "b", "c"]と表示される
["A", "B", "C"]と表示される
["b", "c", "d"]と表示される
例外が発生する
解説:
.freeze した変数に対して破壊的な操作 .map! はできない。ので、正解は「例外が発生する」だね。
code:sh
# ruby gold/ex31/main.rb
gold/ex31/main.rb:2:in `map!': can't modify frozen Array (RuntimeError)
from gold/ex31/main.rb:2:in `<main>'
/icons/hr.icon
もしこの問題が、破壊的ではない操作 .map を使っていたとしたら、
code:gold/ex31/sample.rb
array = array.map{|content| content.succ}
p array
code:sh
# ruby gold/ex31/sample.rb
エラーにならない。
これは、.freezeでは変数の代入による変化は防げないため。
詳しくは↓の通り。
/icons/hr.icon
オブジェクトを凍結(内容の変更を禁止)します。
凍結されたオブジェクトの変更は 例外 RuntimeError を発生させます。 いったん凍結されたオブジェクトを元に戻す方法はありません。
そうだね
凍結されるのはオブジェクトであり、変数ではありません。代入などで変数の指す オブジェクトが変化してしまうことは freeze では防げません。 freeze が防ぐのは、 `破壊的な操作' と呼ばれるもの一般です。
なるほど〜
code:rb
a1 = "foo".freeze
a1 = "bar"
ふむふむ。a1 = "bar" でエラーにならないのね
変数への参照自体を凍結したい 場合は、グローバル変数なら Kernel.#trace_var が使えます。
凍結を解除することはできませんが、Object#dup を使えばほぼ同じ内容の凍結されていない オブジェクトを得ることはできます。
code:rb
b = a.dup
そうだね〜
/icons/hr.icon
collect! {|item| ..} -> self
map! {|item| ..} -> self
collect! -> Enumerator
map! -> Enumerator
各要素を順番にブロックに渡して評価し、その結果で要素を 置き換えます。
ブロックが与えられなかった場合は、自身と map! から生成した Enumerator オブジェクトを返します。
code:rb
irb(main):004:0> enum = arr.map!
ほ〜ん
/icons/hr.icon
succ -> String
next -> String
self の「次の」文字列を返します。
「次の」文字列は、対象の文字列の右端から アルファベットなら アルファベット順(aの次はb, zの次はa, 大文字も同様)に、 数字なら 10 進数(9 の次は 0)とみなして計算されます。
"99" → "100", "AZZ" → "BAA" のような繰り上げも行われます。 このとき負符号などは考慮されません。
code:rb
p "99".succ # => "100"
p "ZZ".succ # => "AAA"
p "a9".succ # => "b0"
p "-9".succ # => "-10"
へ〜〜